
#include <BPainter>
#include <BKeyEvent>
#include <BMouseEvent>
#include <BStyle>
#include <BFont>

#include <BFloatSlider>
#include "member_BFloatSlider.h"

using namespace BWE;

#define member					(*(member_BFloatSlider*)_ptr)
#define member_allocate()		_ptr = new member_BFloatSlider(this)
#define member_release()		delete (member_BFloatSlider*)_ptr

BFloatSlider::BFloatSlider(BWidget* parent) : BWidget(parent)
{
	member_allocate();
}
BFloatSlider::BFloatSlider(Orientation orientation, BWidget* parent) : BWidget(parent)
{
	member_allocate();
	setOrientation(orientation);
}
BFloatSlider::BFloatSlider(float minimum, float maximum, BWidget* parent) : BWidget(parent)
{
	member_allocate();
	setRange(minimum, maximum);
}
BFloatSlider::BFloatSlider(float minimum, float maximum, Orientation orientation, BWidget* parent) : BWidget(parent)
{
	member_allocate();
	setRange(minimum, maximum);
	setOrientation(orientation);
}
BFloatSlider::~BFloatSlider()
{
	member_release();
}

float BFloatSlider::ratio() const
{
	return member.value / (member.maximum - member.minimum);
}

void BFloatSlider::setOrientation(Orientation orientation)
{
	if (member.orientation != orientation)
	{
		member.orientation = orientation;
		member.dirty = true;
		this->fresh();
	}
}
Orientation BFloatSlider::orientation() const
{
	return member.orientation;
}

void BFloatSlider::setIcon(BIcon* icon)
{
	if (member.handleIcon != icon)
	{
		member.handleIcon = icon;
		member.dirty = true;
		this->fresh();
	}
}
BIcon* BFloatSlider::icon() const
{
	return member.handleIcon;
}

void BFloatSlider::setGraph(Graph graph)
{
	if (member.handleGraph != graph)
	{
		member.handleGraph = graph;
		member.dirty = true;
		this->fresh();
	}
}
Graph BFloatSlider::graph() const
{
	return member.handleGraph;
}

void BFloatSlider::setIconSize(const BSize& iconSize)
{
	if (member.handleSize != iconSize)
	{
		member.handleSize = iconSize;
		member.dirty = true;
		this->fresh();
	}
}
const BSize& BFloatSlider::iconSize() const
{
	return member.handleSize;
}

bool BFloatSlider::setValue(float value)
{
	if (value != bLimit(value, member.minimum, member.maximum))
		return false;
	if (member.value != value)
	{
		member.value = value;
		member.dirty = true;
		this->fresh();
		emit(Signal_ValueChanged, value);
	}
	return true;
}
float BFloatSlider::value() const
{
	return member.value;
}

bool BFloatSlider::setRange(float minimum, float maximum)
{
	if (minimum > maximum)
		return false;
	if(member.minimum != minimum || member.maximum != maximum)
	{
		if (member.value < minimum)
			member.value = minimum;
		if (member.value > maximum)
			member.value = maximum;
		member.minimum = minimum;
		member.maximum = maximum;
		member.dirty = true;
		this->fresh();
		BRangef range(minimum, maximum);
		emit(Signal_RangeChanged, range);
	}
	return true;
}
bool BFloatSlider::setRange(const BRangef& range)
{
	return setRange(range.min(), range.max());
}
BRangef BFloatSlider::range() const
{
	return BRangef(member.minimum, member.maximum);
}

bool BFloatSlider::setMinimum(float minimum)
{
	return setRange(minimum, member.maximum);
}
float BFloatSlider::minimum() const
{
	return member.minimum;
}

bool BFloatSlider::setMaximum(float maximum)
{
	return setRange(member.minimum, maximum);
}
float BFloatSlider::maximum() const
{
	return member.maximum;
}

void BFloatSlider::setStep(float step)
{
	if (member.step != step)
	{
		member.step = step;
		emit(Signal_StepChanged, member.step);
	}
}
float BFloatSlider::step() const
{
	return member.step;
}

BSize BFloatSlider::sizeHint() const
{
	BSize hsize = BWidget::sizeHint();
	int width = 16;
	const BFont* font = this->font();
	if (font)
		width = font->size();
	if (member.orientation & Orientation_Horizontal)
	{
		hsize.set(100, width);
	}
	if (member.orientation &  Orientation_Vertical)
	{
		hsize.set(width, 100);
	}
	return hsize;
}

void BFloatSlider::resizeEvent(const BEvent& event)
{
	member.dirty = true;
}

void BFloatSlider::styleEvent(const BEvent& event)
{
	const BStyle* style = this->realStyle();
	if (style)
	{
		const BValue& iconValue = style->value(Value_Icon);
		if (iconValue.valid())
			member.handleIcon = (BIcon*)iconValue;

		const BValue& graphValue = style->value(Value_Graph);
		if (graphValue.valid())
			member.handleGraph = graphValue;

		const BValue& sizeValue = style->value(Value_Handle_Size);
		if (sizeValue.valid())
			member.handleSize = sizeValue;

		const BValue& grooveSizeValue = style->value(Value_Groove_Size);
		if (grooveSizeValue.valid())
		{
			member.grooveSize = grooveSizeValue;
		}
		else
		{
			const BFont* font = this->font();
			member.grooveSize = font->size() / 4;
		}

		BImage* grooveImage = style->image(Image_Groove);
		if (grooveImage)
			member.grooveImage = grooveImage;

		member.dirty = true;
	}
	else
	{
		member.handleIcon = 0;
	}
	this->fresh();
}
void BFloatSlider::freshEvent(const BEvent& event)
{
	if (member.dirty)
	{
		member.freshRects();
	}
}
void BFloatSlider::paintEvent(const BEvent& event)
{
	BPainter painter(this);
	painter.drawBackground();
	painter.drawBorder();
	painter.drawPerch();
	painter.drawMargin();

	if (member.grooveImage)
	{
		painter.drawImage(member.grooveRect, member.grooveImage);
	}
	else
	{
		painter.setColor(Color_Groove);
		painter.drawRect(member.grooveRect);
	}

	if (member.handleIcon)
	{
		BImage* image = member.handleIcon->image(member.handleState);
		painter.setColor(255, 255, 255);
		painter.drawImage(member.handleRect, image);
	}
	else
	{
		StyleRole colorRole = Color_Handle;
		if (member.handleState == State_Pressed)
			colorRole = Color_Handle_Pressed;
		else if (member.handleState == State_Hovered)
			colorRole = Color_Handle_Hovered;
		if (painter.setColor(colorRole))
			painter.drawGraph(member.handleRect % 80, member.handleGraph);
	}
}

void BFloatSlider::mouseEnterEvent(const BMouseEvent& mouseEvent)
{
	if (member.handleState != State_Normal)
	{
		member.handleState = State_Normal;
		this->fresh();
	}
}
void BFloatSlider::mouseLeaveEvent(const BMouseEvent& mouseEvent)
{
	if (member.handleState != State_Normal)
	{
		member.handleState = State_Normal;
		this->fresh();
	}
}

void BFloatSlider::mousePressEvent(const BMouseEvent& mouseEvent)
{
	BPoint pos = mouseEvent.pos();
	if (mouseEvent.button() == Button_Left)
	{
		if (member.handleRect.contain(pos))
		{
			member.handleState = State_Pressed;
			this->fresh();
		}
		else
		{
			BRect crect = this->centerRect();
			if (crect.contain(pos))
			{
				float value = member.valueOfPoint(pos);
				if (setValue(value))
				{
					member.handleState = State_Normal;
					emit(Signal_Sliding, member.value);
				}
			}
		}
	}
}
void BFloatSlider::mouseReleaseEvent(const BMouseEvent& mouseEvent)
{
	if(mouseEvent.button() == Button_Left)
	{
		if (member.handleState == State_Pressed)
		{
			emit(Signal_SlideFinished, member.value);
		}
		member.handleState = State_Normal;
		this->fresh();
	}
}
void BFloatSlider::mouseMoveEvent(const BMouseEvent& mouseEvent)
{
	BRect crect = this->centerRect();
	BPoint pos = mouseEvent.pos();
	if (member.handleState == State_Pressed)
	{
		float value = member.valueOfPoint(pos);
		if (setValue(value))
		{
			emit(Signal_Sliding, member.value);
		}
	}
	else
	{
		State handleState = member.handleRect.contain(pos) ? State_Hovered : State_Normal;
		if (member.handleState != handleState)
		{
			member.handleState = handleState;
			this->fresh();
		}
	}
}
bool BFloatSlider::mouseWheelEvent(const BMouseEvent& wheelEvent)
{
	int whell = wheelEvent.wheel();
	if (setValue(member.value + whell * member.step))
	{
		emit(Signal_Sliding, member.value);
	}
	this->fresh();
	return true;
}

